﻿using System;
using System.Collections.Generic;
using System.Threading;

using NMemcached.Client.Configuration;

namespace NMemcached.Client
{
    /// <summary>
    /// A Memcached connection that provides asynchronous methods for communicating with multiple memcached servers
    /// </summary>
    public class AsyncMemcachedClient : MemcachedClient
    {
        //Set up the delegate variables
        private Func<string, object, DateTime, ResponseCode> _addFunc;
        private Func<string, object, ResponseCode> _appendFunc;
        private Func<string, object, ResponseCode> _prependFunc;
        private Func<string, object, DateTime, ResponseCode> _replaceFunc;
        private Func<string, object, DateTime, ResponseCode> _setFunc;
        private Func<string, object, DateTime, ulong, ResponseCode> _casFunc;
        private Func<string, ulong, ulong> _incFunc;
        private Func<string, ulong, ulong> _decFunc;
        private Func<string, ResponseCode> _delFunc;
        private Func<string, object> _getFunc;
        private Func<string, CacheDetail> _getDetailFunc;
        private Func<string[], IDictionary<string, object>> _getMultipleFunc;
        private Func<string[], IDictionary<string, CacheDetail>> _getMultipleDetailFunc;

        //Set up a reset event
        private AutoResetEvent _resetEvent;
        private bool _disposed;

        /// <summary>
        /// Initializes a new instance of the AsyncMemcachedClient class
        /// </summary>
        /// <param name="servers">A collection of server names without port number</param>
        public AsyncMemcachedClient(params string[] servers)
            : base((IEnumerable<string>)servers)
        {
            this._resetEvent = new AutoResetEvent(true);
        }

        /// <summary>
        /// Initializes a new instance of the AsyncMemcachedClient class
        /// </summary>
        /// <param name="servers">A collection of server names without port number</param>
        public AsyncMemcachedClient(IEnumerable<string> servers)
            : base(servers)
        {
            this._resetEvent = new AutoResetEvent(true);
        }

        /// <summary>
        /// Initializes a new instance of the AsyncMemcachedClient class
        /// </summary>
        /// <param name="synchronousClient">A MemcachedClient from which a new AsyncMemcachedClient is created</param>
        public AsyncMemcachedClient(MemcachedClient synchronousClient)
            : this(synchronousClient.ConnectionProvider)
        {
        }

        /// <summary>
        /// Initializes a new instance of the AsyncConnection class
        /// </summary>
        /// <param name="connectionProvider">The IConnectionProvider used by the client to get server connections</param>
        protected AsyncMemcachedClient(IConnectionProvider connectionProvider)
            : base(connectionProvider)
        {
            this._resetEvent = new AutoResetEvent(true);
        }

        /// <summary>
        /// Begins an asynchronous operation to add an item to the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginAdd(string key, object data, AsyncCallback callback, object state)
        {
            //Create the new delegate
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Add);
            //Signal the reset event
            this._resetEvent.WaitOne();
            //Set the class add delegate to the new one
            this._addFunc = storageDelegate;
            //invoke the new one
            return storageDelegate.BeginInvoke(key, data, DateTime.MinValue, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to add an item to the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginAdd(string key, object data, DateTime expiration, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Add);
            this._resetEvent.WaitOne();
            this._addFunc = storageDelegate;
            return storageDelegate.BeginInvoke(key, data, expiration, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to appends an item and existing cache entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginAppend(string key, object data, AsyncCallback callback, object state)
        {
            Func<string, object, ResponseCode> appendPrepend = new Func<string, object, ResponseCode>(this.Append);
            this._resetEvent.WaitOne();
            this._appendFunc = appendPrepend;
            return appendPrepend.BeginInvoke(key, data, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that stores data for an existing key only if it has not been updated since the last get
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="casUnique">The unique value of an an existing entry</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginCheckAndSet(string key, object data, ulong casUnique, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ulong, ResponseCode> casDelegate = new Func<string, object, DateTime, ulong, ResponseCode>(this.CheckAndSet);
            this._resetEvent.WaitOne();
            this._casFunc = casDelegate;
            return casDelegate.BeginInvoke(key, data, DateTime.MinValue, casUnique, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that stores data for an existing key only if it has not been updated since the last get
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <param name="casUnique">The unique value of an an existing entry</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginCheckAndSet(string key, object data, DateTime expiration, ulong casUnique, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ulong, ResponseCode> casDelegate = new Func<string, object, DateTime, ulong, ResponseCode>(this.CheckAndSet);
            this._resetEvent.WaitOne();
            this._casFunc = casDelegate;
            return casDelegate.BeginInvoke(key, data, expiration, casUnique, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that decreases a numeric cached value at the specified key by the specified amount
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="count">The amount by which the value is decremented</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginDecrement(string key, ulong count, AsyncCallback callback, object state)
        {
            Func<string, ulong, ulong> decDelegate = new Func<string, ulong, ulong>(this.Decrement);
            this._resetEvent.WaitOne();
            this._decFunc = decDelegate;
            return decDelegate.BeginInvoke(key, count, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that removes a cache entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginDelete(string key, AsyncCallback callback, object state)
        {
            Func<string, ResponseCode> deleteDelegate = new Func<string, ResponseCode>(this.Delete);
            this._resetEvent.WaitOne();
            this._delFunc = deleteDelegate;
            return deleteDelegate.BeginInvoke(key, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that reads a single entry from the cache
        /// </summary>
        /// <param name="key">The key for which the data is read</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginGet(string key, AsyncCallback callback, object state)
        {
            Func<string, object> getDelegate = new Func<string, object>(this.Get);
            this._resetEvent.WaitOne();
            this._getFunc = getDelegate;
            return getDelegate.BeginInvoke(key, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that reads multiple entries from the cache
        /// </summary>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <param name="keys">A list of keys for which the data is read</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginGetMultiple(AsyncCallback callback, object state, params string[] keys)
        {
            Func<string[], IDictionary<string, object>> getDelegate = new Func<string[], IDictionary<string, object>>(this.Get);
            this._resetEvent.WaitOne();
            this._getMultipleFunc = getDelegate;
            return getDelegate.BeginInvoke(keys, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that reads a single entry from the cache with detail information
        /// </summary>
        /// <param name="key">The key for which the data is read</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginGetWithDetail(string key, AsyncCallback callback, object state)
        {
            Func<string, CacheDetail> getDetailDelegate = new Func<string, CacheDetail>(this.GetWithDetail);
            this._resetEvent.WaitOne();
            this._getDetailFunc = getDetailDelegate;
            return getDetailDelegate.BeginInvoke(key, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that reads multiple entries from the cache with detail information
        /// </summary>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <param name="keys">A list of keys for which the data is read</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginGetWithDetailMultiple(AsyncCallback callback, object state, params string[] keys)
        {
            Func<string[], IDictionary<string, CacheDetail>> getDetailDelegate = new Func<string[], IDictionary<string, CacheDetail>>(this.GetWithDetail);
            this._resetEvent.WaitOne();
            this._getMultipleDetailFunc = getDetailDelegate;
            return getDetailDelegate.BeginInvoke(keys, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation that increases a numeric cached value at the specified key by the specified amount
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="count">The amount by which the value is incremented</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginIncrement(string key, ulong count, AsyncCallback callback, object state)
        {
            Func<string, ulong, ulong> incDelegate = new Func<string, ulong, ulong>(this.Increment);
            this._resetEvent.WaitOne();
            this._incFunc = incDelegate;
            return incDelegate.BeginInvoke(key, count, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to prepend an item to an existing cahce entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginPrepend(string key, object data, AsyncCallback callback, object state)
        {
            Func<string, object, ResponseCode> appendPrepend = new Func<string, object, ResponseCode>(this.Prepend);
            this._resetEvent.WaitOne();
            this._prependFunc = appendPrepend;
            return appendPrepend.BeginInvoke(key, data, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to replace an item in the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginReplace(string key, object data, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Replace);
            this._resetEvent.WaitOne();
            this._replaceFunc = storageDelegate;
            return storageDelegate.BeginInvoke(key, data, DateTime.MinValue, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to replace an item in the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginReplace(string key, object data, DateTime expiration, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Replace);
            this._resetEvent.WaitOne();
            this._replaceFunc = storageDelegate;
            return storageDelegate.BeginInvoke(key, data, expiration, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to set an item to the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginSet(string key, object data, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Set);
            this._resetEvent.WaitOne();
            this._setFunc = storageDelegate;
            return storageDelegate.BeginInvoke(key, data, DateTime.MinValue, callback, state);
        }

        /// <summary>
        /// Begins an asynchronous operation to set an item to the cache
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <param name="callback">The AsyncCallback delegate</param>
        /// <param name="state">An object that contains state information for the request</param>
        /// <returns>An IAsyncResult that references the asynchronous operation</returns>
        public IAsyncResult BeginSet(string key, object data, DateTime expiration, AsyncCallback callback, object state)
        {
            Func<string, object, DateTime, ResponseCode> storageDelegate = new Func<string, object, DateTime, ResponseCode>(this.Set);
            this._resetEvent.WaitOne();
            this._setFunc = storageDelegate;
            return storageDelegate.BeginInvoke(key, data, expiration, callback, state);
        }

        /// <summary>
        /// Creates a new AsyncMemcachedClient from the application configuration
        /// </summary>
        /// <returns>A new AsyncMemcachedClient defined by the application configuration</returns>
        /// <remarks>
        /// <para>An AsynchMemcachedClient instance can be created using settings read from the applicaiton or web configuration file. The built-in configuration uses the included connection provider and does not have the ability to use custom connection providers.</para>
        /// <para>Example:</para>
        /// <code>
        /// &lt;configSections&gt;
        ///     &lt;section name="nmemcached.client" type="NMemcached.Client.Configuration.NMemcachedConfigurationSection, NMemcached.Client" /&gt;
        /// &lt;/configSections&gt;
        /// &lt;nmemcached.client xmlns="http://memcached.protectedvoid.com/2011/06/configuration"&gt;
        ///     &lt;connectionProvider&gt;
        ///         &lt;add server="localhost" compressionAlgorithm="Deflate" /&gt;
        ///         &lt;add server="someRemoteServer" port="15455" compressionAlgorithm="GZip" /&gt;
        ///     &lt;/connectionProvider&gt;
        /// &lt;/nmemcached.client&gt;
        /// </code>
        /// <para>Note: An XSD is in the source tree and included with the binary distributions for intellisense.</para>
        /// <para>The ConnectionProvider element has an optional "hashAlgorithm" attribute that specifies the algorithm to use for they consistent hashing algorithm. The possible values are:</para>
        /// <list type="bullet">
        ///     <item>
        ///         <term>fnv32</term>
        ///         <description>The FNV algorithm that produces a 32 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>fnv1a32 (default)</term>
        ///         <description>The FNV1a algorithm that produces a 32 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>fnv64</term>
        ///         <description>The FNV algorithm that produces a 64 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>fnv1a64</term>
        ///         <description>The FNV1a algorithm that produces a 64 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>md5</term>
        ///         <description>The MD5 algorithm that produces a 128 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>sha1</term>
        ///         <description>The SHA1 algorithm that produces a 160 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>sha256</term>
        ///         <description>The SHA1 algorithm that produces a 256 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>sha384</term>
        ///         <description>The SHA1 algorithm that produces a 384 bit hash value</description>
        ///     </item>
        ///     <item>
        ///         <term>sha512</term>
        ///         <description>The SHA1 algorithm that produces a 512 bit hash value</description>
        ///     </item>
        /// </list>
        /// <para>Each "add" child element of "connectionProvider" represents a single Memcached server. The only required attribute is the "server" attribute that must contian the host name ir IP address of the destination. Any combination of optional attributes can be provided to configure individual server connections:</para>
        /// <list type="bullet">
        ///     <item>
        ///         <term>port</term>
        ///         <description>A valid TCP/UDP port number. (default: "11211")</description>
        ///     </item>
        ///     <item>
        ///         <term>protocol</term>
        ///         <description>The Memcached communication protocol. Valid values are "text" and "binary". (default: "text")</description>
        ///     </item>
        ///     <item>
        ///         <term>transport</term>
        ///         <description>The transport protocol for the connection. Valid values are "tcp", "udp", "connectionOriented", "connectionless". (default: "tcp")</description>
        ///     </item>
        ///     <item>
        ///         <term>useCompression</term>
        ///         <description>Determines if data compression is used for items sent to the cache. (default: "false")</description>
        ///     </item>
        ///     <item>
        ///         <term>compressionAlgorithm</term>
        ///         <description>The algorithm to use for data compression if "useCompression" is set to "true". Valid values are "GZip" and "Deflate". (default: "GZip")</description>
        ///     </item>
        ///     <item>
        ///         <term>paylodEncoding</term>
        ///         <description>The character encoding of the data sent to the cache. Value values are "ASCII", "BigEndianUnicode", "Unicode", "UTF32", "UTF7", and "UTF8". (default: "UTF8")</description>
        ///     </item>
        ///     <item>
        ///         <term>protocolEncoding</term>
        ///         <description>The character encoding of the memcached protocol. Valid values are the same as the payloadEncoding attribute. (default: "UTF8")</description>
        ///     </item>
        ///     <item>
        ///         <term>sendTimeout</term>
        ///         <description>The number of milliseconds to wait to send data before throwing an exception. (default: 5000)</description>
        ///     </item>
        ///     <item>
        ///         <term>receiveTimeout</term>
        ///         <description>The number of milliseconds to wait to receive data before throwing an exception. (default: 5000)</description>
        ///     </item>
        /// </list>
        /// </remarks>
        public static new AsyncMemcachedClient Create()
        {
            return new AsyncMemcachedClient(NMemcachedConfiguration.CreateClient());
        }

        /// <summary>
        /// Ends a pending asynchronous add operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndAdd(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._addFunc.EndInvoke(ar);
            }
            finally
            {
                this._addFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous append operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndAppend(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._appendFunc.EndInvoke(ar);
            }
            finally
            {
                this._appendFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous check and set operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndCheckAndSet(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._casFunc.EndInvoke(ar);
            }
            finally
            {
                this._casFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous decrement operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The value of the cache entry after the decrement operation</returns>
        public ulong EndDecrement(IAsyncResult ar)
        {
            ulong result;
            try
            {
                result = this._decFunc.EndInvoke(ar);
            }
            finally
            {
                this._decFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous delete operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndDelete(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._delFunc.EndInvoke(ar);
            }
            finally
            {
                this._delFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous get operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The item read from the cache</returns>
        public object EndGet(IAsyncResult ar)
        {
            object result = null;
            try
            {
                result = this._getFunc.EndInvoke(ar);
            }
            finally
            {
                this._getFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous get with detail operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The item read from the cache with detail information</returns>
        public CacheDetail EndGetWithDetail(IAsyncResult ar)
        {
            CacheDetail result = null;
            try
            {
                result = this._getDetailFunc.EndInvoke(ar);
            }
            finally
            {
                this._getDetailFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous multi get operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>A keyed collection of items read from the cache</returns>
        public IDictionary<string, object> EndGetMultiple(IAsyncResult ar)
        {
            IDictionary<string, object> result = null;
            try
            {
                result = this._getMultipleFunc.EndInvoke(ar);
            }
            finally
            {
                this._getMultipleFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous multi get with detail operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>A keyed collection of items read from the cache with detail information</returns>
        public IDictionary<string, CacheDetail> EndGetWithDetailMultiple(IAsyncResult ar)
        {
            IDictionary<string, CacheDetail> result = null;
            try
            {
                result = this._getMultipleDetailFunc.EndInvoke(ar);
            }
            finally
            {
                this._getMultipleDetailFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous increment operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The value of the cache entry after the increment operation</returns>
        public ulong EndIncrement(IAsyncResult ar)
        {
            ulong result;
            try
            {
                result = this._incFunc.EndInvoke(ar);
            }
            finally
            {
                this._incFunc = null;
                this._resetEvent.Set();
            }
            return result;
        }

        /// <summary>
        /// Ends a pending asynchronous prepend operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndPrepend(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._prependFunc.EndInvoke(ar);
            }
            finally
            {
                this._prependFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous replace operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndReplace(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._replaceFunc.EndInvoke(ar);
            }
            finally
            {
                this._replaceFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Ends a pending asynchronous set operation
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information and any user defined data for the asynchronous operation</param>
        /// <returns>The response of the storage operation</returns>
        public ResponseCode EndSet(IAsyncResult ar)
        {
            ResponseCode responseCode;
            try
            {
                responseCode = this._setFunc.EndInvoke(ar);
            }
            finally
            {
                this._setFunc = null;
                this._resetEvent.Set();
            }
            return responseCode;
        }

        /// <summary>
        /// Releases the unmanaged resources used by the AsyncMemcachedClient, and optionally disposes of the managed resources
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to releases only unmanaged resources</param>
        protected override void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                }
                if (this._resetEvent != null)
                {
                    this._resetEvent.Close();
                }
                base.Dispose();
                this._disposed = true;
            }
        }
    }
}